<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
</head>

<body>
    <div id="app"></div>
    <button id="btn">按钮</button>
    <p>a b c d e f g</p>
    <p>a b e d c h f g</p>
    <script>
        // 创建虚拟DOM节点（VNode）的函数，简单模拟
        function createVNode(tag, props, children) {
            return {
                tag,
                props,
                children,
                key: props?.key || null
            };
        }

      // 判断两个VNode是否是相同类型节点（这里简单根据tag和key判断）
        function isSameVNodeType(n1, n2) {
            return n1.tag === n2.tag && n1.key === n2.key;
        }

        // 挂载单个VNode到真实DOM容器中
        function mountVNode(vnode, container, anchor = null) {
            const el = document.createElement(vnode.tag);
            if (vnode.props) {
                for (const key in vnode.props) {
                    el.setAttribute(key, vnode.props[key]);
                }
            }
            if (vnode.children) {
                if (Array.isArray(vnode.children)) {
                    vnode.children.forEach(child => {
                        mountVNode(child, el);
                    });
                } else {
                    el.textContent = vnode.children;
                }
            }
            container.appendChild(el);
            vnode.el = el;
            return el;
        }

        // 卸载VNode对应的真实DOM元素
        function unmountVNode(vnode) {
            if (vnode.el && vnode.el.parentNode) {
                vnode.el.parentNode.removeChild(vnode.el);
            }
        }

        // 对比新旧子节点（核心的diff逻辑，简化版）
        function patchKeyedChildren(c1, c2, container) {
            console.log('c1', c1);
            console.log('c2', c2);
            // console.log('container',container);
            let i = 0;
            let e1 = c1.length - 1;
            let e2 = c2.length - 1;

            // 同时从头部开始比较
            while (i <= e1 && i <= e2) {
                const n1 = c1[i];
                const n2 = c2[i];
                if (isSameVNodeType(n1, n2)) {
                    // console.log(n1,n2);

                    patch(n1, n2, n1.el.parentNode);
                } else {
                    break;
                }
                i++;
            }

            // 同时从尾部开始比较
            while (e1 >= i && e2 >= i) {
                const n1 = c1[e1];
                const n2 = c2[e2];
                if (isSameVNodeType(n1, n2)) {
                    // console.log(n1,n2);
                    patch(n1, n2, n1.el.parentNode);
                } else {
                    break;
                }
                e1--;
                e2--;
            }
            console.log('i', i);
            console.log('e1', e1);
            console.log('e2', e2);
            if (i > e1) {
                console.log('如果旧节点先遍历完，说明新节点有新增的，依次挂载新节点');
                if (i <= e2) {
                    const nextPos = e1 + 1;
                    const anchor = e2 + 1 < c2.length ? c2[e2 + 1].el : null;
                    while (i <= e2) {
                        console.log('挂载', i, c2[i]);
                        mountVNode(c2[i], container, anchor);
                        i++;
                    }
                }
            } else if (i > e2) {
                console.log('如果新节点先遍历完，说明旧节点有多余的，依次卸载旧节点');
                while (i <= e1) {
                    console.log('卸载', i, c1[i]);

                    unmountVNode(c1[i]);
                    i++;
                }
            } else {
                console.log('中间部分的复杂比较和处理，构建新旧节点的映射关系，基于key来判断复用、移动等情况');
                const s1 = i;
                const s2 = i;
                const keyToNewIndexMap = new Map();
                for (let i = s2; i <= e2; i++) {
                    const nextChild = c2[i];
                    keyToNewIndexMap.set(nextChild.key, i);
                }
                // e d c h
                console.log('keyToNewIndexMap', keyToNewIndexMap);

                // let j;
                // *4.2 记录一下新老元素的相对下标
                const toBePatched = e2 - s2 + 1;
                console.log('toBePatched', toBePatched);

                const newIndexToOldIndexMap = new Array(toBePatched).fill(0);
                console.log(newIndexToOldIndexMap);



                for (let i = s1; i <= e1; i++) {
                    const prevChild = c1[i];
                    console.log('prevChild', prevChild);

                    let newIndex = keyToNewIndexMap.get(prevChild.key);
                    console.log('newIndex', newIndex);

                    if (newIndex === undefined) {
                        // 旧节点在新节点中找不到对应的key，说明该节点需要被卸载
                        console.log('卸载',prevChild);
                        
                        unmountVNode(prevChild);
                    } else {
                        newIndexToOldIndexMap[newIndex - s2] = i;
                        console.log('不卸载',newIndexToOldIndexMap);
                    }
                }
                console.log('`````````````');
                
                for (let i = s2; i <= e2; i++) {
                    const nextChild = c2[i];
                    console.log('nextChild',nextChild);
                    
                    let oldIndex = newIndexToOldIndexMap[i - s2];
                    console.log('oldIndex',oldIndex);
                    if (oldIndex === 0) {
                        
                        const anchor = i < e2 ? c2[i + 1].el : null;
                        console.log('// 如果旧索引为0，说明是新增节点，进行挂载',nextChild,container,anchor);
                        mountVNode(nextChild, container, anchor);
                    } else {
                        const prevChild = c1[oldIndex];
                        console.log('prevChild',prevChild);
                        if (isSameVNodeType(prevChild, nextChild)) {
                          
                            console.log('  // 如果是相同类型节点，进行更新',prevChild,nextChild,prevChild.el.parentNode);
                            
                            patch(prevChild, nextChild, prevChild.el.parentNode);
                        } else {
                            console.log('// 如果类型不同，先卸载旧节点，再挂载新节点');
                            
                            
                            unmountVNode(prevChild);
                            const anchor = i < e2 ? c2[i + 1].el : null;
                            mountVNode(nextChild, container, anchor);
                        }
                    }
                }
            }
        }

        // 对比更新两个VNode（入口函数）
        function patch(n1, n2, container) {
            // console.log('n1',n1);
            // console.log('n2',n2);
            // console.log('container',container);

            if (n1 === n2) {
                // console.log('一样');
                return;
            }
            if (!n1) {
                // console.log('n1 kong 挂在新的');
                mountVNode(n2, container);
            } else if (!n2) {
                // console.log('n2 kong 删除');
                unmountVNode(n1);
            } else if (isSameVNodeType(n1, n2)) {
                // console.log('key 相同');

                if (n1.tag === 'text') {
                    if (n1.children !== n2.children) {
                        n1.el.textContent = n2.children;
                    }
                } else {
                    // console.log('!text');

                    const el = n1.el;
                    patchProps(n1.props, n2.props, el);
                    patchChildren(n1, n2, el);
                }
            } else {
                // console.log('isSameVNodeType 不同');
                unmountVNode(n1);
                mountVNode(n2, container);
            }
        }

        // 对比更新节点属性
        function patchProps(oldProps, newProps, el) {
            // console.log('oldProps',oldProps);
            // console.log('newProps',newProps);
            // console.log(el);

            if (oldProps) {
                for (const key in oldProps) {
                    if (!newProps || !(newProps[key])) {
                        el.removeAttribute(key);
                    }
                }
            }
            if (newProps) {
                for (const key in newProps) {
                    if (oldProps[key] !== newProps[key]) {
                        el.setAttribute(key, newProps[key]);
                    }
                }
            }
        }

        // 对比更新子节点（根据子节点类型调用不同的处理逻辑）
        function patchChildren(n1, n2, el) {
            const c1 = n1.children;
            const c2 = n2.children;
            if (Array.isArray(c1) && Array.isArray(c2)) {
                patchKeyedChildren(c1, c2, el);
            } else if (Array.isArray(c1)) {
                while (c1.length) {
                    unmountVNode(c1.pop());
                }
            } else if (Array.isArray(c2)) {
                mountVNode(c2[0], el);
            } else if (c1 !== c2) {
                if (c1) {
                    n1.el.textContent = c2;
                } else {
                    el.textContent = c2;
                }
            }
        }


        let oldVNodes = createVNode('ul', {}, [
            createVNode('li', { key: 'a' }, 'a'),
            createVNode('li', { key: 'b' }, 'b'),

            createVNode('li', { key: 'c' }, 'c'),
            createVNode('li', { key: 'd' }, 'd'),
            createVNode('li', { key: 'e' }, 'e'),

            createVNode('li', { key: 'f' }, 'f'),
            createVNode('li', { key: 'g' }, 'g'),
        ])

        let newVNodes = createVNode('ul', {}, [
            createVNode('li', { key: 'a' }, 'a'),
            createVNode('li', { key: 'b' }, 'b'),

            createVNode('li', { key: 'e' }, 'e'),
            createVNode('li', { key: 'd' }, 'd'),
            createVNode('li', { key: 'c' }, 'c'),
            createVNode('li', { key: 'h' }, 'h'),

            createVNode('li', { key: 'f' }, 'f'),
            createVNode('li', { key: 'g' }, 'g'),
        ])


        const app = document.getElementById('app');
        patch(null, oldVNodes, app);
        console.log('a b c d e f g');
        console.log('a b e d c h f g');


        let btn = document.getElementById('btn')
        btn.onclick = () => {
            patch(oldVNodes, newVNodes, app);
        }

    </script>
</body>

</html>